#pragma once

#include <concepts>

namespace solar
{
    class CommonPara;
    class ScenePara;
    class SimulationPara;
    class Sun;
    class HeliostatPara;
    class Receiver;
    class Heliostat;
    class Vec3;
    using ComputeSigmaCallback = void(*)(void *);

    template <typename T>
    concept ClassWithComputeSigma =
        requires(SimulationPara* p1, ScenePara* p2, HeliostatPara* p3, CommonPara* p4, Sun* p5,
                 Heliostat* p6, Receiver* p7, T t) {
            { t.computeSigma(p1, p2, p3, p4, p5, p6, p7) } -> std::same_as<void>;
        };

    template <typename T>
    concept ClassWithComputeSigma2 =
        requires(SimulationPara* p1, ScenePara* p2, HeliostatPara* p3, CommonPara* p4, Sun* p5,
                 Heliostat* p6, Receiver* p7, std::size_t num_helios, T t, ComputeSigmaCallback c) {
            { t.computeSigma(p1, p2, p3, p4, p5, p6, p7, num_helios, c) } -> std::same_as<void>;
        };

    template <typename T>
    concept ClassWithComputeSigmaWithGlass =
        requires(SimulationPara* p1, ScenePara* p2, HeliostatPara* p3, CommonPara* p4, Sun* p5,
                 Heliostat* p6, Receiver* p7, T t) {
            { t.computeSigmaWithGlass(p1, p2, p3, p4, p5, p6, p7) } -> std::same_as<void>;
        };

    template <typename T>
    concept ClassWithArea = requires(T t) {
        { t.area() } -> std::same_as<double>;
    };

    template <typename T>
    concept ClassWithNormal = requires(T t) {
        { t.normal() } -> std::same_as<Vec3>;
    };

    template <typename T>
    concept ClassWithNormalAt = requires(T t, Vec3* vec) {
        { t.normalAt(vec) } -> std::same_as<Vec3>;
    };

    template <typename T>
    concept ClassWithCenter = requires(T t) {
        { t.center() } -> std::same_as<Vec3>;
    };

    template <typename T1, typename T2>
    concept ClassWithDistanceTo = requires(T1 t1, T2 t2) {
        { t1.distanceTo(t2) } -> std::same_as<double>;
    };

    template <typename T1, typename T2>
    concept ClassWithFocusCenterAt = requires(T1 t1, T2 t2) {
        { t1.focusCenterAt(t2) } -> std::same_as<Vec3>;
    };
} // namespace solar
